
from micropython import const
from i2c_helpers import CBits, RegisterStruct


__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/jposada202020/MicroPython_HTS221.git"


_WHO_AM_I = const(0x0F)

_CTRL_REG1 = const(0x20)
_CTRL_REG2 = const(0x21)
_CTRL_REG3 = const(0x22)
_STATUS_REG = const(0x27)
# some addresses are anded to set the  top bit so that multi-byte reads will work
_HUMIDITY_OUT_L = const(0x28 | 0x80)  # Humidity output register (LSByte)
_TEMP_OUT_L = const(0x2A | 0x80)  # Temperature output register (LSByte)

_H0_RH_X2 = const(0x30)  # Humididy calibration LSB values
_H1_RH_X2 = const(0x31)  # Humididy calibration LSB values

_T0_DEGC_X8 = const(0x32)  # First byte of T0, T1 calibration values
_T1_DEGC_X8 = const(0x33)  # First byte of T0, T1 calibration values
_T1_T0_MSB = const(0x35)  # Top 2 bits of T0 and T1 (each are 10 bits)

_H0_T0_OUT = const(0x36 | 0x80)  # Humididy calibration Time 0 value
_H1_T1_OUT = const(0x3A | 0x80)  # Humididy calibration Time 1 value

_T0_OUT = const(0x3C | 0x80)  # T0_OUT LSByte
_T1_OUT = const(0x3E | 0x80)  # T1_OUT LSByte


ONE_SHOT = const(0b00)
RATE_1_HZ = const(0b01)
RATE_7_HZ = const(0b10)
RATE_12_5_HZ = const(0b11)
data_rate_values = (ONE_SHOT, RATE_1_HZ, RATE_7_HZ, RATE_12_5_HZ)

BDU_DISABLED = const(0b0)
BDU_ENABLED = const(0b1)
block_data_update_values = (BDU_DISABLED, BDU_ENABLED)


class HTS221:
    _device_id = RegisterStruct(_WHO_AM_I, "<B")
    _boot_bit = CBits(1, _CTRL_REG2, 7)
    enabled = CBits(1, _CTRL_REG1, 7)
    """Controls the power down state of the sensor. Setting to `False` will shut the sensor down"""
    _data_rate = CBits(2, _CTRL_REG1, 0)
    _block_data_update = CBits(1, _CTRL_REG1, 2)

    _one_shot_bit = CBits(1, _CTRL_REG2, 0)
    _raw_temperature = RegisterStruct(_TEMP_OUT_L, "<h")
    _raw_humidity = RegisterStruct(_HUMIDITY_OUT_L, "<h")

    _t0_deg_c_x8_lsbyte = CBits(8, _T0_DEGC_X8, 0)
    _t1_deg_c_x8_lsbyte = CBits(8, _T1_DEGC_X8, 0)
    _t1_t0_deg_c_x8_msbits = CBits(4, _T1_T0_MSB, 0)

    _t0_out = RegisterStruct(_T0_OUT, "<h")
    _t1_out = RegisterStruct(_T1_OUT, "<h")

    _h0_rh_x2 = RegisterStruct(_H0_RH_X2, "<B")
    _h1_rh_x2 = RegisterStruct(_H1_RH_X2, "<B")

    _h0_t0_out = RegisterStruct(_H0_T0_OUT, "<h")
    _h1_t0_out = RegisterStruct(_H1_T1_OUT, "<h")

    def __init__(self, i2c, address: int = 0x5F) -> None:
        self._i2c = i2c
        self._address = address

        if self._device_id != 0xBC:
            raise RuntimeError("Failed to find the HTS221 sensor")

        self._boot()
        self.enabled = True
        self.data_rate = RATE_12_5_HZ
        self._block_data_update = BDU_ENABLED

        t1_t0_msbs = self._t1_t0_deg_c_x8_msbits
        self.calib_temp_value_0 = self._t0_deg_c_x8_lsbyte
        self.calib_temp_value_0 |= (t1_t0_msbs & 0b0011) << 8

        self.calibrated_value_1 = self._t1_deg_c_x8_lsbyte
        self.calibrated_value_1 |= (t1_t0_msbs & 0b1100) << 6

        self.calib_temp_value_0 >>= 3  # divide by 8 to remove x8
        self.calibrated_value_1 >>= 3  # divide by 8 to remove x8

        self.calib_temp_meas_0 = self._t0_out
        self.calib_temp_meas_1 = self._t1_out

        self.calib_hum_value_0 = self._h0_rh_x2
        self.calib_hum_value_0 >>= 1  # divide by 2 to remove x2

        self.calib_hum_value_1 = self._h1_rh_x2
        self.calib_hum_value_1 >>= 1  # divide by 2 to remove x2

        self.calib_hum_meas_0 = self._h0_t0_out
        self.calib_hum_meas_1 = self._h1_t0_out

    def _boot(self) -> None:
        self._boot_bit = True
        while self._boot_bit:
            pass

    @property
    def relative_humidity(self) -> float:
        """The current relative humidity measurement in %rH"""
        calibrated_value_delta = self.calib_hum_value_1 - self.calib_hum_value_0
        calibrated_measurement_delta = self.calib_hum_meas_1 - self.calib_hum_meas_0

        calibration_value_offset = self.calib_hum_value_0
        calibrated_measurement_offset = self.calib_hum_meas_0
        zeroed_measured_humidity = self._raw_humidity - calibrated_measurement_offset

        correction_factor = calibrated_value_delta / calibrated_measurement_delta

        adjusted_humidity = (
            zeroed_measured_humidity * correction_factor + calibration_value_offset
        )

        return adjusted_humidity

    @property
    def temperature(self) -> float:
        """The current temperature measurement in Celsius"""

        calibrated_value_delta = self.calibrated_value_1 - self.calib_temp_value_0
        calibrated_measurement_delta = self.calib_temp_meas_1 - self.calib_temp_meas_0

        calibration_value_offset = self.calib_temp_value_0
        calibrated_measurement_offset = self.calib_temp_meas_0
        zeroed_measured_temp = self._raw_temperature - calibrated_measurement_offset

        correction_factor = calibrated_value_delta / calibrated_measurement_delta

        adjusted_temp = (
            zeroed_measured_temp * correction_factor
        ) + calibration_value_offset

        return adjusted_temp

    @property
    def data_rate(self) -> str:
        values = ("ONE_SHOT", "RATE_1_HZ", "RATE_7_HZ", "RATE_12_5_HZ")
        return values[self._data_rate]

    @data_rate.setter
    def data_rate(self, value: int) -> None:
        if value not in data_rate_values:
            raise ValueError("Value must be a valid data_rate setting")
        self._data_rate = value

    def take_measurements(self) -> None:
        """Update the value of :attr:`relative_humidity` and :attr:`temperature` by taking a single
        measurement. Only meaningful if :attr:`data_rate` is set to ``ONE_SHOT``"""
        self._one_shot_bit = True
        while self._one_shot_bit:
            pass

    @property
    def block_data_update(self) -> str:
        values = ("BDU_DISABLED", "BDU_ENABLED")
        return values[self._block_data_update]

    @block_data_update.setter
    def block_data_update(self, value: int) -> None:
        if value not in block_data_update_values:
            raise ValueError("Value must be a valid block_data_update setting")
        self._block_data_update = value